package hfu.pm.bll;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

import com.googlecode.objectify.Key;

import hfu.pm.domain.DAO;
import hfu.pm.domain.DAOT;
import hfu.pm.domain.DAOT.Transactable;
import hfu.pm.entities.Account;
import hfu.pm.entities.Invitation;
import hfu.pm.entities.Message;
import hfu.pm.entities.Room;
import hfu.pm.entities.Subscription;

public class ChatLogic {

	static DAO dao = new DAO();

	// check if there is a private room for those 2
	public static boolean hasPrivateChatRoom(Account acc1, Account acc2) {

		Iterable<Room> privateRooms = dao.ofy().query(Room.class)
				.filter("_private", Boolean.TRUE);

		for (Room tmpRoom : privateRooms) {
			if (inRoom(tmpRoom, acc1) && inRoom(tmpRoom, acc2)) {
				return true;
			}
		}
		return false;
	}
	
	public static boolean hasPrivateChatRoom(String accKey1, String accKey2) {
		return hasPrivateChatRoom(dao.ofy().get(new Key<Account>(accKey1)), dao.ofy().get(new Key<Account>(accKey2)));
	}

	// return a private room of those 2
	public static Room getPrivateChatRoom(Account acc1, Account acc2) {

		Iterable<Room> privateRooms = dao.ofy().query(Room.class)
				.filter("_private", Boolean.TRUE);

		for (Room tmpRoom : privateRooms) {
			if (inRoom(tmpRoom, acc1) && inRoom(tmpRoom, acc2)) {
				return tmpRoom;
			}
		}
		return createPrivateChatRoom(acc1, acc2);
	}

	// create & return a private room for those 2
	private static Room createPrivateChatRoom(Account acc1, Account acc2) {

		// create new private room
		final Room room = new Room();
		room.setPrivate(true);
		room.setName("Private");

		// create subscription for acc1
		final Subscription s1 = new Subscription();

		s1.setAccount(new Key<Account>(Account.class, acc1.getId()));

		// create subscription for acc2
		final Subscription s2 = new Subscription();

		s2.setAccount(new Key<Account>(Account.class, acc2.getId()));

		// store 3 objects in a transaction
		DAOT.runInTransaction(new Transactable() {

			@Override
			public void run(DAOT daot) {
				dao.ofy().put(room);
				s1.setRoom(new Key<Room>(Room.class, room.getId()));
				dao.ofy().put(s1);
				s2.setRoom(new Key<Room>(Room.class, room.getId()));
				dao.ofy().put(s2);
			}
		});

		return room;
	}

	// create & return a new public room
	public static Room createPublicChatRoom(Account creator, String name) {

		final Room room = new Room();
		room.setPrivate(false);
		room.setName(name);

		final Subscription subscription = new Subscription();
		subscription
				.setAccount(new Key<Account>(Account.class, creator.getId()));

		DAOT.runInTransaction(new Transactable() {

			@Override
			public void run(DAOT daot) {
				dao.ofy().put(room);
				subscription.setRoom(new Key<Room>(Room.class, room.getId()));
				dao.ofy().put(subscription);
			}
		});

		return room;
	}

	// check if acc is already in room
	public static boolean inRoom(Room room, Account acc) {

		Iterable<Subscription> subscriptions = dao.ofy()
				.query(Subscription.class).filter("account", acc);
		Key<Room> key = new Key<Room>(Room.class, room.getId());
		for (Subscription tmpSubs : subscriptions) {
			if (key.equals(tmpSubs.getRoom())) {
				return true;
			}
		}
		return false;
	}

	// add an account to a room
	public static void addNewUserToRoom(final Room room, Account acc) {
		if (!inRoom(room, acc)) {
			// acc not in room
			final Subscription subscription = new Subscription();
			subscription
					.setAccount(new Key<Account>(Account.class, acc.getId()));
			subscription.setRoom(new Key<Room>(Room.class, room.getId()));

			DAOT.runInTransaction(new Transactable() {

				@Override
				public void run(DAOT daot) {
					room.setLastActivity();
					dao.ofy().put(room);
					dao.ofy().put(subscription);
				}
			});
		}
	}

	// add an account to a room followed by an invitation
	private static void addNewUserToRoom(final Invitation invitation) {

		final Account acc = dao.ofy().get(invitation.getReceiver());
		final Room room = dao.ofy().get(invitation.getRoom());
		final Subscription subscription = new Subscription();
		subscription.setAccount(new Key<Account>(Account.class, acc.getId()));
		subscription.setRoom(new Key<Room>(Room.class, room.getId()));

		DAOT.runInTransaction(new Transactable() {

			@Override
			public void run(DAOT daot) {
				room.setLastActivity();
				dao.ofy().put(room);
				dao.ofy().put(subscription);
				dao.ofy().delete(invitation);
			}
		});

	}

	// Get subscribed public rooms of an account
	public static List<Room> getSubscribedPublicRoomsOf(Account acc) {

		List<Subscription> tmp = dao.ofy().query(Subscription.class)
				.filter("account", acc).list();
		List<Room> room = new ArrayList<Room>();
		for (Subscription subscription : tmp) {
			Room temp = dao.ofy().get(subscription.getRoom());
			if (!temp.isPrivate())
				room.add(temp);
		}
		Collections.sort(room);
		return room;

	}

	// get messages in room from a fixed time
	public static List<Message> getMessages(Room room, Date timestamp) {
		return dao.ofy().query(Message.class).filter("room", room)
				.order("timestamp").filter("timestamp >=", timestamp).list();
	}

	// get last X messages in room
	public static List<Message> getLastXMessages(Room room, int x) {
		return dao.ofy().query(Message.class).filter("room", room)
				.order("-timestamp").limit(x).list();
	}

	// send a message
	public static void sendMessage(Account acc, final Room room, String content) {
		final Message mess = new Message();
		mess.setRoom(new Key<Room>(Room.class, room.getId()));
		mess.setSender(new Key<Account>(Account.class, acc.getId()));
		mess.setContent(content);

		DAOT.runInTransaction(new Transactable() {

			@Override
			public void run(DAOT daot) {
				dao.ofy().put(mess);
				room.setLastActivity();
				dao.ofy().put(room);
			}
		});

	}

	// get list of accounts in room
	public static List<Account> listOfUsers(Room room) {
		List<Subscription> subs = dao.ofy().query(Subscription.class)
				.filter("room", room).list();
		List<Account> result = new ArrayList<Account>();
		for (Subscription subscription : subs) {
			result.add(dao.ofy().get(subscription.getAccount()));
		}
		return result;
	}

	// get a room from its key
	public static Room getRoom(String key) {
		Key<Room> k = new Key<Room>(key);
		return dao.ofy().get(k);
	}

	// invite new user to a room
	public static void inviteToRoom(Room room, Account sender, Account receiver) {
		if (!inRoom(room, receiver)) {
			Invitation invi = new Invitation();
			invi.setRoom(new Key<Room>(Room.class, room.getId()));
			invi.setReceiver(new Key<Account>(Account.class, receiver.getId()));
			invi.setSender(new Key<Account>(Account.class, sender.getId()));
			
			dao.ofy().put(invi);
		}
	}

	// respond to a room's invitation
	public static void respondToARoomInvitation(Invitation invitation,
			boolean accept) {
		if (accept) {
			addNewUserToRoom(invitation);
		} else
			dao.ofy().delete(invitation);
	}
	
	// get subscribed users
	public static List<Account> getSubscribedUsers(Room room) {
		List<Subscription> s = dao.ofy().query(Subscription.class).filter("room", room).list();
		List<Account> result = new ArrayList<Account>();
		for (Subscription sub : s) {
			result.add(dao.ofy().get(sub.getAccount()));
		}
		return result;
	}
	
	// get invitation list of
	public static List<Invitation> getInvitationList(Account acc) {
		return dao.ofy().query(Invitation.class).filter("receiver", acc).list();
	}
	
	public static Invitation getInvitation(String key) {
		Key<Invitation> k = new Key<Invitation>(key);
		return dao.ofy().get(k);
	}
}
